package test;

import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;

import dsa.LinkedList;
import dsa.ListIterator;

/**
 * @author Amanda Webster
 */

public class AmandaWebsterTest {

	private LinkedList list;
	private LinkedList clone;
	
	private static final String zero = "Tywin", one = "Cersei", two = "Tyrion", three = "Jaime";
	
	/**
	 * Creates a new linked list, adds four string objects to the list, then creates a clone of the list.
	 */
	@Before
	public void setUp() throws Exception {
		list = new LinkedList();
		
		list.addLast(one);
		list.addLast(two);
		list.addLast(three);
		list.addFirst(zero);
		
		clone =  (LinkedList) list.clone();
	}
	
	/**
	 * Tests that the cloned list was copied correctly by checking that the elements are 
	 * in the correct position within the list.
	 */
	@Test
	public void cloneTest() {
		assertTrue(clone.get(2).equals(two));
		assertTrue(clone.get(1).equals(one));
		assertTrue(clone.get(0).equals(zero));
		assertTrue(clone.get(3).equals(three));
		
		assertFalse(clone.get(0).equals(three));
		
		assertTrue(clone.getFirst().equals(zero));
		assertTrue(clone.getLast().equals(three));
	}
	
	/**
	 * Tests that the array generated by the toArray() method and the cloned list 
	 * have matching elements in matching indices.
	 */
	@Test
	public void cloneAndToArray() {
		Object[] array = list.toArray();
		
		for (int i = 0; i < list.size(); i++) {
			assertTrue(array[i].equals(clone.get(i)));
			assertTrue(array[i].equals(list.get(i)));
			assertTrue(list.get(i).equals(clone.get(i)));
		}
	}
	
	/**
	 * Tests the contain() method by checking that objects known to be part of the list return true,
	 * and objects known not to be contained in the list return false.
	 */
	@Test
	public void containsTest() {
		assertTrue(list.contains(one) == true);
		assertTrue(list.contains(three) == true);
		
		assertFalse(list.contains("Mary") == true);
	}
	
	/**
	 * Tests the get() method by checking that the object returned is equal 
	 * to the object known to be at that position in the list.
	 */
	@Test
	public void getTest() {
		assertTrue(list.get(2).equals(two));
		assertTrue(list.get(1).equals(one));
		assertTrue(list.get(0).equals(zero));
		assertTrue(list.get(3).equals(three));
		
		assertFalse(list.get(0).equals(three));
		
		assertTrue(list.getFirst().equals(zero));
		assertTrue(list.getLast().equals(three));
	}
	
	/**
	 * Tests the indexOf() method by checking the returned index against the known index of the object in the list.
	 */
	@Test
	public void indexOfTest() {
		assertTrue(list.indexOf(one) == 1);
		assertTrue(list.indexOf(zero) == 0);
		assertTrue(list.indexOf(one) == 1);
		assertTrue(list.indexOf(three) == 3);
		assertTrue(list.indexOf(two) == 2);
		
		assertFalse(list.indexOf(zero) == 1);
	}
	
	/**
	 * Tests the removeFirst() method by removing the elements at those positions, and checking
	 * that the list shrinks correctly in correspondence to the removal. 
	 */
	@Test
	public void removeTest() {
		assertTrue(list.size() == 4);
		
		list.removeFirst();
		
		assertTrue(list.get(0).equals(one));		
		assertTrue(list.get(1).equals(two));
		assertTrue(list.get(2).equals(three));
		
		assertTrue(list.size() == 3);
	}
	
	/**
	 * Tests the set() method by first checking the element known to be at position 3 in the list,
	 * then replacing it with a new string object and checking that the new object is found at position 3 in the list.
	 */
	@Test
	public void setTest() {
		assertTrue(list.get(3).equals(three));
		list.set(3, "Arya");
		assertTrue(list.get(3).equals("Arya"));
	}
	
	/**
	 * Compares the size of the list with the number of elements known to be in the list to test the size() method.
	 */
	@Test
	public void sizeTest() {
		assertTrue(list.size() == 4);
		assertTrue(clone.size() == 4);
		
		list.addFirst("Joanna");
		assertTrue(list.size() == 5);
		assertFalse(clone.size() == 5);
		
		list.removeFirst();
		assertTrue(list.size() == 4);
		assertTrue(list.size() == 4);
	}
	
	/**
	 * Tests the toArray() method by creating an array of the elements in the linked list, then checking that
	 * the element at a given index in the array matches the element known to be at that position in the list.
	 */
	@Test
	public void toArrayTest() {
		Object[] array = list.toArray();
		
		assertTrue(array[2].equals(list.get(2)));
		assertTrue(array[0].equals(list.get(0)));
		assertTrue(array[3].equals(list.get(3)));
		assertTrue(array[1].equals(list.get(1)));
	}
	
	/**
	 * Tests the listIterator() method by creating an iterator, removing and adding elements, and traversing the list
	 * in both directions.
	 */
	@Test
	public void iteratorAddTest() {
		ListIterator it = list.listIterator(0);
		
		it.add("Hello");
		assertTrue(it.hasNext());
		String test = (String) it.next();
		assertFalse(test.equals("Hello"));
		assertTrue(test.equals(zero));
		it.previous();
		it.next();
		it.next();
		it.next();
		it.add("Hi again");
		test = (String) it.next();
		assertFalse(test.equals("Hi again"));
		assertTrue(test.equals(three));
	}
	
	@Test
	public void iteratorRemoveTest() {
		ListIterator it = list.listIterator(0);
		assertTrue(it.hasNext());
		it.next();
		assertTrue(it.hasPrevious());
		it.remove();
		assertTrue(it.next().equals(one));
		assertTrue(it.hasNext());
		it.next();
		it.remove();
		assertTrue(it.next().equals(three));
	}
	
	@Test
	public void iteratorSetTest() {
		ListIterator it = list.listIterator(0);
		it.next();
		it.set("Hello");
		assertTrue(it.hasPrevious());
		assertTrue(it.previous().equals("Hello"));
		assertTrue(it.next().equals("Hello"));
		assertTrue(it.hasNext());
		it.next();
		assertTrue(it.hasNext());
		it.next();
		it.set("Hi again");
		assertTrue(it.previous().equals("Hi again"));
		assertTrue(it.next().equals("Hi again"));
		
	}
	
	@Test
	public void iteratorStartingPointTest() {
		ListIterator it = list.listIterator(0);
		assertTrue(it.next().equals(zero));
		assertTrue(it.next().equals(one));
		
		it = list.listIterator(2);
		assertTrue(it.next().equals(two));
		assertTrue(it.next().equals(three));
		assertTrue(it.previous().equals(three));
		assertTrue(it.previous().equals(two));
		assertTrue(it.previous().equals(one));
		assertTrue(it.previous().equals(zero));
	}
	
	@Test
	public void iteratorNextAndPreviousTest() {
		ListIterator it = list.listIterator(0);
		assertTrue(it.next().equals(zero));
		assertTrue(it.next().equals(one));
		assertTrue(it.next().equals(two));
		assertTrue(it.next().equals(three));
		assertTrue(it.previous().equals(three));
		assertTrue(it.previous().equals(two));
		assertTrue(it.previous().equals(one));
		assertTrue(it.previous().equals(zero));
		
	}
	
	@Test
	public void iteratorIndexTest() {
		
	}
}